package com.yc.springmvc;

import com.google.gson.Gson;
import com.yc.springmvc.controller.IndexController;
import com.yc.springmvc.controller.ParamDO;
import lombok.Data;
import ognl.Ognl;
import ognl.OgnlException;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.http.converter.json.GsonBuilderUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;


public class MyDispatchServlet extends HttpServlet {

    Map<String, Object> ioc = new HashMap<>();

    {
        ioc.put("index", new IndexController());
        ioc.put("do", new ParamDO());
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //调用父类的 没有重写父类的doXxx方法 报405错误
//        super.service(req, resp);
        Handler handler = handlerMapping(req);
        if (handler == null) {
            //404错误
            defaultServletHandler(req, resp);
            return;
        }
        Object res = handlerAdapter(handler, req, resp);
        System.out.println("=============================== " + res + "==================================");


        //1.405
        //2.对res对象的返回 => 1) json,  2)页面跳转
        if (returnJson(handler)) {
            Gson gson = new Gson();
            String json = gson.toJson(res);
            resp.setContentType("application/json;charset=utf-8");
            resp.getWriter().append(json);
            resp.flushBuffer();
        } else if (returnHtml(handler)) {
            //html
            String path = null;
            if (res instanceof ModelAndView) {
                path = ((ModelAndView) res).getViewName();
            } else if (res instanceof String) {
                path = (String) res;
            }
            //只处理响应重定向
            //发送重定向
            if (path.contains(":")) {
                path = path.substring("redirect:".length());
            }
            resp.sendRedirect(path);
            resp.flushBuffer();
        } else {
            //TODO
        }
    }

    private void defaultServletHandler(HttpServletRequest req, HttpServletResponse resp) {
        String path = req.getServletPath();
        //A.html
        URL url = this.getClass().getClassLoader().getResource("");
        String filePath = url.getPath();
        filePath = URLDecoder.decode(filePath);
        File dir = new File(filePath.substring(1));
        File webRoot = dir.getParentFile().getParentFile();
        File file = new File(webRoot, path);

        try (FileInputStream fis = new FileInputStream(file);
             OutputStream out = resp.getOutputStream()) {

            byte[] bytes = new byte[1024];
            int count;
            while ((count = fis.read(bytes)) > 0) {
                out.write(bytes, 0, count);
            }

            if (path.endsWith(".html")) {
                resp.setContentType("text/html;charset=utf-8");
            } else if (path.endsWith(".css")) {
                resp.setContentType("text/css;charset=utf-8");
            } else if (path.endsWith(".js")) {
                resp.setContentType("application/javascript;charset=utf-8");
            } else if(path.endsWith(".jpg")){
                resp.setContentType("image/jpeg");
            } else {
                //其他文件类型，这里假设默认为 text/plain
                resp.setContentType("text/plain;charset=utf-8");
            }
        } catch (IOException e) {
            throw new RuntimeException("静态文件输出异常", e);
        }
    }


    //获取处理器（控制器+方法）
    private Handler handlerMapping(HttpServletRequest req) {
        Handler handler = new Handler();
        //获取请求地址
        String path = req.getServletPath();
        //查找控制器对象
        try {
            ioc.values().forEach(item -> {
                RequestMapping requestMapping = item.getClass().getAnnotation(RequestMapping.class);
                String annoPath = null;
                if (requestMapping != null) {
                    //多个配置地址  或者为path = ...
                    annoPath = requestMapping.path().length > 0 ? requestMapping.path()[0] : requestMapping.value()[0];
                    if (annoPath.startsWith("/") == false) {
                        //补 “ / ”
                        annoPath = "/" + annoPath;
                    }
                }
                String finalAnnoPath = annoPath == null ? "" : annoPath;
                Arrays.stream(item.getClass().getDeclaredMethods()).forEach(method -> {
                    RequestMapping requestMapping1 = method.getAnnotation(RequestMapping.class);
                    if (requestMapping1 == null) {
                        //跳过此次迭代
                        return;
                    }
                    String methodPath = requestMapping1.path().length > 0 ? requestMapping1.path()[0] : requestMapping1.value()[0];
                    if (methodPath.startsWith("/") == false) {
                        //补 “ / ”
                        methodPath = "/" + methodPath;
                    }
                    if (path.equals(finalAnnoPath + methodPath)) {
//                    System.out.println("================================== 路径正确 =============================");
//                    System.out.println("！！！！！！！！！！！");
                        handler.setController(item);
                        handler.setMethod(method);
                        throw new RuntimeException("");
                    }
                });
            });
        } catch (RuntimeException e) {
            return handler;
        }
        return null;
    }


    public boolean returnJson(Handler handler) {
        RestController resetController = handler.getController().getClass().getAnnotation(RestController.class);
        Controller controller = handler.getController().getClass().getAnnotation(Controller.class);
        ResponseBody responseBody = handler.getMethod().getAnnotation(ResponseBody.class);
        //方法返回类型
        Class<?> returnType = handler.getMethod().getReturnType();

        return (resetController != null && !returnType.isAssignableFrom(ModelAndView.class)) ||
                (controller != null && responseBody != null);
    }

    private boolean returnHtml(Handler handler) {
        RestController resetController = handler.getController().getClass().getAnnotation(RestController.class);
        Controller controller = handler.getController().getClass().getAnnotation(Controller.class);
        ResponseBody responseBody = handler.getMethod().getAnnotation(ResponseBody.class);
        //方法返回类型
        Class<?> returnType = handler.getMethod().getReturnType();

        return (resetController != null && returnType.isAssignableFrom(ModelAndView.class)) ||
                (controller != null && responseBody == null);
    }

    //执行处理器
    public Object handlerAdapter(Handler handler, HttpServletRequest req, HttpServletResponse resp) {
        //解析参数
        List<String> names = getParameterNames(handler.getMethod());
        //方法参数值数组
        List<Object> values = new ArrayList<>();
        for (int i = 0; i < handler.getMethod().getParameters().length; i++) {
            String name = names.get(i);
            String value = req.getParameter(name);
            Parameter parameter = handler.getMethod().getParameters()[i];
            //转型
            if (parameter.getType().equals(String.class)) {
                values.add(value);  //字符串
            } else if (parameter.getType().equals(Integer.class)) {
                values.add(Integer.valueOf(value));  //int
            } else if (parameter.getType().equals(Double.class)) {
                values.add(Double.valueOf(value));  //Double
            } else if (parameter.getType().equals(Float.class)) {
                values.add(Float.valueOf(value));  //Float
            } else if (parameter.getType().equals(Long.class)) {
                values.add(Long.valueOf(value));  //Long
            } else if (parameter.getType().isArray()) {
                String[] array = req.getParameterValues(name); // 数组
                Class componentType = parameter.getType().getComponentType();
                if (componentType.equals(String.class)) {
                    values.add(array);
                } else if (componentType.equals(Integer.TYPE)) {
                    Integer[] toArray = Arrays.stream(array).map(Integer::parseInt).toArray(Integer[]::new);
                    values.add(toArray);
                } else if (componentType.equals(Double.TYPE)) {
                    Double[] toArray = Arrays.stream(array).map(Double::parseDouble).toArray(Double[]::new);
                    values.add(toArray);
                }
            } else if (parameter.getType().isAssignableFrom(HttpServletRequest.class)) {
                values.add(req);
            } else if (parameter.getType().isAssignableFrom(HttpServletResponse.class)) {
                values.add(req);
            } else if (parameter.getType().isAssignableFrom(HttpSession.class)) {
                values.add(req.getSession());
            } else {
                RequestBody requestBody = parameter.getAnnotation(RequestBody.class);
                if (requestBody != null){
                    //参数类型
                    Class<?> type = parameter.getType();
                    //获取body的内容
                    try {
                        BufferedReader reader = req.getReader();
                        String line;
                        StringBuilder sb = new StringBuilder();
                        while ((line = reader.readLine()) != null) {
                            sb.append(line);
                        }
                        String json = sb.toString();
                        Gson gson = new Gson();
                        Object obj  = gson.fromJson(json, type);
                        values.add(obj);
                    }catch (Exception e){
                        throw new RuntimeException("设置请求体参数异常");
                    }
                }else{
                    //实体对象
                    Object obj = createObject(parameter, req);
                    values.add(obj);
                }
            }
        }
        handler.getMethod().setAccessible(true);
        try {
            return handler.getMethod().invoke(handler.getController(), values.toArray());
        } catch (Exception e) {
            throw new RuntimeException("控制器方法执行错误！", e);
        }
    }

    private Object createObject(Parameter parameter, HttpServletRequest req) {
        Object object = null;
        try {
            object = parameter.getType().newInstance();
            setFieldValue(object, req, null);
        } catch (Exception e) {
            throw new RuntimeException("自动装箱错误！！！");
        }
        return object;
    }


    void setFieldValue(Object object, HttpServletRequest req, String fieldPah) throws IllegalAccessException, InstantiationException, OgnlException {
        //field => name,age...
        for (Field field : object.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            String parameterName = field.getName();
            if (fieldPah != null) {
                parameterName = fieldPah + "." + field.getName();
            }
            String value = req.getParameter(parameterName);
            if (value == null && containSubField(req, parameterName)) {
                //实体对象
                Object subObject = field.getType().newInstance();
                //递归调用
                setFieldValue(subObject, req, parameterName);
                field.set(object, subObject);
                continue;
            }
            if (field.getType().isAssignableFrom(String.class) ||
                    field.getType().isAssignableFrom(Integer.class) || field.getType().isAssignableFrom(int.class) ||
                    field.getType().isAssignableFrom(Long.class) || field.getType().isAssignableFrom(long.class) ||
                    field.getType().isAssignableFrom(Boolean.class) || field.getType().isAssignableFrom(boolean.class) ||
                    field.getType().isAssignableFrom(Double.class) || field.getType().isAssignableFrom(Double.class) ||
                    field.getType().isAssignableFrom(Float.class) || field.getType().isAssignableFrom(Float.class)
            ) {
                Ognl.setValue(field.getName(), object, value);
            } else if (field.getType().isAssignableFrom(List.class)) {

            } else {
                //其他
            }
        }
    }

    //判断参数中是否有以parameterName开头的参数
    private boolean containSubField(HttpServletRequest req, String fieldPath) {
        Enumeration<String> names = req.getParameterNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            //正则表达式  类似  "fieldPath.Xxx"
            String regex = fieldPath + "\\.\\w+";
            if (name.matches(regex)) {
                return true;
            }
        }
        return false;
    }

    @Data
    class Handler {
        Object controller;
        Method method;
    }


    public List<String> getParameterNames(Method method) {
        LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
        // 获取当前类的所有方法
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
        List<String> names = new ArrayList<>();
        if (parameterNames.length > 0 && parameterNames != null) {
            Collections.addAll(names, parameterNames);
        }
        return names;
    }


}
